跳到主要内容

MyBatis 配置详情

配置项的编写顺序

参考资料 官方-mybatis-配置 参考资料 XML约束——DTD约束

一般配置文件叫 mybatis-config.xml

MyBatis 官方定义配置项的编写顺序(XML 的元素是可以设置顺序的,参考 XML的DTD约束

* configuration(配置)
* properties(属性)
* settings(设置)
* typeAliases(类型别名)
* typeHandlers(类型处理器)
* objectFactory(对象工厂)
* plugins(插件)
* environments(环境配置)
* environment(环境变量)
* transactionManager(事务管理器)
* dataSource(数据源)
* databaseIdProvider(数据库厂商标识)
* mappers(映射器)

编写配置文件

同 Spring 一样,配置文件是通过 XML 来更改的

其中先设定 environments 环境设置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<!-- development 表示开发环境,可以设置几套环境 -->
<environment id="development">

<!-- 设置事务管理器 -->
<transactionManager type="JDBC"/>

<!-- 配置 DataSource 这里的类型使用数据池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/studyJDBC?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true&amp;useServerPrepStmts=true"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>

<!--每一个Mapper.xml都需要在这里注册-->
<mappers>
<mapper resource="com\alsritter\dao\UserMapper.xml"/>
</mappers>
</configuration>

环境配置

MyBatis 可以配置多种环境,但是 每个 SqlSessionFactory 实例只能选择一种环境

如果想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推

为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。

// 获取 sqlSession 工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

不过可以使用默认环境,就无需显示指定了

事务管理器

<transactionManager type="JDBC"/>

在 MyBatis 中有两种类型的事务管理器 JDBC、MANAGED

1、JDBC 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

2、MANAGED 这个配置不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。所以需要手动打开提交,将 closeConnection 属性设置为 false



数据源

就是 dbcpC3P0druid 这些工具:它封装了数据库参数,连接数据库,需要设置数据库url,用户名和密码,此时就相当于一个代理数据库

DataSource 包含 连接池连接池管理 两个部分

有三种的数据源类型 UNPOOLED、POOLED、JNDI

<dataSource type="POOLED">

1、UNPOOLED–没有连接池 数据源的实现都会每次请求时打开和关闭连接。

2、POOLED–连接池(默认) 没什么好说,JDBC 里已经讲清楚了

3、JNDI 无卵用,给以前的EJB这些容器使用

读取配置文件的工具类

// 加载核心配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);

// 获取 sqlSession 工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 获取 sqlSession 对象
SqlSession sqlSession = sqlSessionFactory.openSession();

// 执行 sql 语句
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();

根据上述的方法把读取配置文件的操作整合成一个工具类:

public class MyBatisUtils {

private static SqlSessionFactory sqlSessionFactory ;

static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}

public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}

导入 Mapper 的几种方式

映射方式实际上也有多种

<!-- 推荐使用这种方式 -->
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<!-- 注意: -->
<!-- 接口和他的Mapper配置文件必须同名 -->
<!-- 接口和他的Mapper配置文件必须在同个包下 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<!-- 注意: -->
<!-- 接口和他的Mapper配置文件必须同名 -->
<!-- 接口和他的Mapper配置文件必须在同个包下 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>

读取 properties 文件

因为 DataSource 的一些属性都是可外部配置且动态替换的,所以一般丢到 properties 文件里动态读取

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/studyJDBC?useUnicode=true&characterEncoding=utf8&useSSL=true&useServerPrepStmts=true
username=root

然后在 XML 里导入 properties 配置文件(注意XML的元素是有顺序的,要把 properties 写在 environments 上面)

<!-- 可以不在properties里写一些配置,而留到这里写 -->
<!-- 但是优先使用外部参数,所以当有相同参数时,优先加载外部参数 -->
<properties resource="db.properties">
<property name="password" value="123"/>
</properties>


<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>

类型别名(typeAliases)

之前写自定义类型时需要写全类名,而使用了类型别名之后可以省下这功夫

参考--官方自带的别名

曾经:

<select id="getUserById" parameterType="int" resultType="com.alsritter.pojo.User">
select * from users where id = #{id};
</select>

使用类型别名有两种方式

直接指定某个类起别名

<typeAliases>
<typeAlias alias="User" type="com.alsritter.pojo.User"/>
</typeAliases>

指定一个包路径,让其自动扫描这个包下面的类

<typeAliases>
<package name="com.alsritter.pojo"/>
</typeAliases>

使用注解的方式,这个是对扫描包的补充(配合上面那个扫描包使用的),使扫描包的对象可以使用自定名

@Alias("hello") // 只是@Alias这个注解,下面的是Lombok的
@Data@ToString@AllArgsConstructor@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}

需要指定不同别名时可以使用第一种方式,其他的直接扫描包就好了

现在就可以直接使用别名了

<!-- 就可以直接使用别名(大小写都可以)来代指这个自定义类了 -->
<select id="getUserById" parameterType="int" resultType="user">
select * from users where id = #{id};
</select>

日志工厂

系统设置没有默认值,所以需要手动选择一个日志系统

SLF4J LOG4J LOG4J2 JDK_LOGGING COMMONS_LOGGING STDOUT_LOGGING NO_LOGGING

<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

返回自增 ID

参考资料 深入浅出mybatis之useGeneratedKeys参数用法 参考资料 mybatis获取自增主键

Mybatis 配置文件 useGeneratedKeys 参数只针对 insert 语句生效,默认为 false。当设置为 true 时,表示如果插入的表以自增列为主键,则允许 JDBC 支持自动生成主键,并可将自动生成的主键返回。

获取自增主键

mybatis 提供了两种方式获取数据库自增主键:

1、通过useGeneratedKeys、keyProperty、keyColumn

2、通过 <selectKey> 标签。

下面主要讲第一种方式

在获取数据库自增主键时,我们一般会把数据库主键设置为自动增长的,如有一张数据库表 user 定义如下:

CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,#自增
`name` varchar(255) NOT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

对应的 User 类:

public class User {
private Integer id;
private String name;
private Integer age;
//...setters and getters
//...to string...
}

useGeneratedKeys、keyProperty、keyColumn属性,仅能在 <insert> 或者 <update> 元素中使用。作用分别如下所示:

1、useGeneratedKeys:这会令 MyBatis 使用 JDBC 的 ResultSet.getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。

2、keyProperty:唯一标记一个属性,MyBatis 会通过 ResultSet.getGeneratedKeys 的返回值,默认:unset 如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。

3、keyColumn:通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。

使用例:获取插入的单条记录的自增id:

<insert id="insert" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (name, age) VALUES (#{name}, #{age})
</insert>

测试代码如下:

@Test
public void testInsert() {
User user = new User();
user.setName("test");
user.setAge(27);
System.out.println("插入前 id:" + user.getId());
sqlSession.insert("com.test.mybatis.batch.mappers.UserMapper.insert", user);
System.out.println("插入后:" + user.getId());
}

控制台输出:

插入前:User{id=null, name='test', age=27}
23:16:10.935 [main] DEBUG com.test.mybatis.batch.mappers.UserMapper.insert - ==> Preparing: INSERT INTO user ( name, age) VALUES ( ?, ?)
23:16:10.998 [main] DEBUG com.test.mybatis.batch.mappers.UserMapper.insert - ==> Parameters: test(String), 27(Integer)
23:16:11.107 [main] DEBUG com.test.mybatis.batch.mappers.UserMapper.insert - <== Updates: 1
插入后:User{id=1, name='test', age=27}

获取批量插入的记录自增id

<insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user (id, name, age) VALUES
<foreach collection="list" item="user" index="index" separator="," >
(#{user.id}, #{user.name}, #{user.age})
</foreach>
</insert>

测试代码如下:

@Test
public void testBatchInsert() {
User user1 = new User();
user1.setName("tianshouzhi");
user1.setAge(27);
User user2 = new User();
user2.setName("wangxiaoxiao");
user2.setAge(26);
List<User> userList = new ArrayList<User>();
userList.add(user1);
userList.add(user2);
System.out.println("插入前:" + userList);
sqlSession.insert("com.tianshouzhi.mybatis.batch.mappers.UserMapper.batchInsert", userList);
System.out.println("插入后:" + userList);
}

运行程序后输出:

插入前:[User{id=null, name='tianshouzhi', age=27}, User{id=null, name='wangxiaoxiao', age=26}]
23:16:42.894 [main] DEBUG com.tianshouzhi.mybatis.batch.mappers.UserMapper.batchInsert - ==> Preparing: INSERT INTO user (id, name, age) VALUES (?, ?, ?) , (?, ?, ?)
23:16:42.956 [main] DEBUG com.tianshouzhi.mybatis.batch.mappers.UserMapper.batchInsert - ==> Parameters: null, tianshouzhi(String), 27(Integer), null, wangxiaoxiao(String), 26(Integer)
23:16:43.112 [main] DEBUG com.tianshouzhi.mybatis.batch.mappers.UserMapper.batchInsert - <== Updates: 2
插入后:[User{id=2, name='tianshouzhi', age=27}, User{id=3, name='wangxiaoxiao', age=26}]

在配置项上开启

开启方式一:在设置项开启:对于支持自动生成记录主键的数据库,如:MySQL,SQL Server,此时设置 useGeneratedKeys 参数值为true,在执行添加记录之后可以获取到数据库自动生成的主键 ID。

实际上,在 settings 元素中设置 useGeneratedKeys 是一个全局参数,但是只会对接口映射器产生影响,对 xml 映射器不起效

<settings>
<!--
允许JDBC支持自动生成主键,需要驱动兼容。
如果设置为true则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。
-->
<setting name="useGeneratedKeys" value="true" />
</settings>

此时,在接口映射中添加记录之后将返回主键ID。

public interface TestMapper {
// 受全局useGeneratedKeys参数控制,添加记录之后将返回主键id
@Insert("insert into test(name,descr,url,create_time,update_time) values(#{name},#{descr},#{url},now(),now())")
Integer insertOneTest(Test test);
}

但是,请注意如果此时在接口映射器中又明确设置了 useGeneratedKeys 参数,那么注解映射器中的 useGeneratedKeys 参数值将覆盖 settings 元素中设置的全局 useGeneratedKeys 参数值。

举个例子:先在 settings 元素中设置全局 useGeneratedKeys 参数值为 true,再在接口映射器中设置 useGeneratedKeys 参数值为 false,添加记录之后将不能返回注解ID。

// 在接口映射器中设置的useGeneratedKeys参数值将会覆盖在settings元素中设置全局useGeneratedKeys参数值
@Options(useGeneratedKeys = false, keyProperty = "id", keyColumn = "id")
@Insert("insert into test(name,descr,url,create_time,update_time) values(#{name},#{descr},#{url},now(),now())")
Integer insertOneTest(Test test);

另外,在 settings 元素中设置的全局 useGeneratedKeys 参数对于 xml 映射器无效。如果希望在 xml 映射器中执行添加记录之后返回主键 ID,则必须在 xml 映射器中明确设置 useGeneratedKeys 参数值为 true

在 Insert 映射器上开启

方式二:直接在对应的 Insert 标签上使用

<!-- 插入数据:返回记录的id值 -->
<insert id="insertOneTest" parameterType="org.test.mybatis.model.Test" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into test(name,descr,url,create_time,update_time)
values(#{name},#{descr},#{url},now(),now())
</insert>

xml 映射器中配置的 useGeneratedKeys 参数只会对 xml 映射器产生影响,且在 settings 元素中设置的全局 useGeneratedKeys 参数值对于 xml 映射器不产生任何作用

在注解上开启

方式三:在注解上使用

// 设置useGeneratedKeys为true,返回数据库自动生成的记录主键id
@Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
@Insert("insert into test(name,descr,url,create_time,update_time) values(#{name},#{descr},#{url},now(),now())")
Integer insertOneTest(Test test);

其它项目设置

<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>